package com.xiangxiao.rpan.storage.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.github.tobato.fastdfs.domain.fdfs.FileInfo;
import com.github.tobato.fastdfs.domain.fdfs.MetaData;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.xiangxiao.rpan.storage.constant.ResponseCode;
import com.xiangxiao.rpan.storage.constant.ResponseMessage;
import com.xiangxiao.rpan.storage.dto.StorePathDto;
import com.xiangxiao.rpan.storage.entity.FileItemResult;
import com.xiangxiao.rpan.storage.enums.FileTypeEnum;
import com.xiangxiao.rpan.storage.enums.StorageTypeEnum;
import com.xiangxiao.rpan.storage.exception.StorageExcetion;
import com.xiangxiao.rpan.storage.param.FastdfsParam;
import com.xiangxiao.rpan.storage.service.AbstractBaseFileService;
import com.xiangxiao.rpan.storage.utils.StringUtil;
import com.xiangxiao.rpan.utils.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileNotFoundException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @auther xiangxiao
 * @email 573768011@qq.com
 * @data 2023/11/9 19:48
 */
@Service
public class FastdfsFileServiceImpl extends AbstractBaseFileService<FastdfsParam> {
  @Autowired
  private FastFileStorageClient fastFileStorageClient;
  @Override
  public void init() {

  }

  private static void checkPathSecurity(String path) {
    // 路径中不能包含 .. 不然可能会获取到上层文件夹的内容
    if (StrUtil.containsAny(path, "../", "..\\")) {
      throw new IllegalArgumentException("文件路径存在安全隐患: " + path);
    }
  }

  private FileItemResult fileToFileItem(File file, String folderPath) {
    SimpleDateFormat sdf = new SimpleDateFormat(DateUtil.FORMAT_DATETIME_CN);

    FileItemResult fileItemResult = new FileItemResult();
    fileItemResult.setType(file.isDirectory() ? FileTypeEnum.FOLDER : FileTypeEnum.FILE);
    fileItemResult.setTime(sdf.format(file.lastModified()));
    fileItemResult.setSize(file.length());
    fileItemResult.setName(file.getName());
    fileItemResult.setPath(folderPath);

    if (fileItemResult.getType() == FileTypeEnum.FILE) {
      fileItemResult.setUrl(getDownloadUrl(StringUtil.concat(folderPath, file.getName())));
    } else {
      fileItemResult.setSize(null);
    }
    return fileItemResult;
  }

  @Override
  public List<FileItemResult> fileList(String folderPath) throws Exception {
    this.checkPathSecurity(folderPath);

    List<FileItemResult> fileItemList = new ArrayList<>();

    String fullPath = StringUtil.concat( param.getFilePath() + folderPath);

    System.out.println("全路径为:" + fullPath);
    File file = new File(fullPath);

    if (!file.exists()) {
      throw new FileNotFoundException("文件不存在");
    }

    File[] files = file.listFiles();

    if (files == null) {
      return fileItemList;
    }
    for (File f : files) {
      fileItemList.add(fileToFileItem(f, folderPath));
    }

    return fileItemList;
  }

  @Override
  public String getDownloadUrl(String pathAndName) {
    return param.getDomain() + param.getFilePath() + pathAndName;
  }

  @Override
  public StorageTypeEnum getStorageTypeEnum() {
    return StorageTypeEnum.FASTDFS;
  }

  @Override
  public FileItemResult getFileItem(String pathAndName) {
    SimpleDateFormat sdf = new SimpleDateFormat(DateUtil.FORMAT_DATETIME_CN);
    String fileName = FileUtil.getName(pathAndName);
    String parentPath = StringUtil.getParentPath(pathAndName);
    String fullPath = StringUtil.concat(param.getFilePath(), pathAndName);
    StorePath storePath = StorePathDto.praseFromUrl(fullPath);

    FileInfo fileInfo = fastFileStorageClient.queryFileInfo(storePath.getGroup(), storePath.getPath());
    FileItemResult fileItemResult = new FileItemResult();
    fileItemResult.setName(fileName);
    fileItemResult.setSize(fileInfo.getFileSize());
    fileItemResult.setTime(sdf.format(fileInfo.getCreateTime()));
    fileItemResult.setType(FileTypeEnum.FILE);
    fileItemResult.setPath(parentPath);
    fileItemResult.setUrl(getDownloadUrl(pathAndName));
    return fileItemResult;
  }

  @Override
  public boolean newFolder(String path, String name) {
    return false;
  }

  @Override
  public boolean deleteFile(String path, String name) {
    checkPathSecurity(path);
    checkNameSecurity(name);

    String fullPath = StringUtil.concat(param.getFilePath(), path, name);
    StorePath storePath = StorePathDto.praseFromUrl(fullPath);

    fastFileStorageClient.deleteFile(storePath.getGroup(), storePath.getPath());
    return true;
  }

  @Override
  public boolean deleteFolder(String path, String name) {
    // fastdfs分布式存储不支持删除下属的文件夹,删了就乱套了
    throw new StorageExcetion(ResponseMessage.NOT_SUPPORT_FASTDFS_DELETE_FLODER, ResponseCode.NOT_SUPPORT_FASTDFS_DELETE_FLODER);
  }

  @Override
  public boolean renameFile(String path, String name, String newName) {
    // fastdfs分布式存储不支持重命名文件
    throw new StorageExcetion(ResponseMessage.NOT_SUPPORT_FASTDFS_RENAME_FILE, ResponseCode.NOT_SUPPORT_FASTDFS_RENAME_FILE);
  }

  @Override
  public boolean renameFolder(String path, String name, String newName) {
    // fastdfs分布式存储不支持重命名下属的文件夹,改了就乱套了
    throw new StorageExcetion(ResponseMessage.NOT_SUPPORT_FASTDFS_RENAME_FLODER, ResponseCode.NOT_SUPPORT_FASTDFS_RENAME_FLODER);
  }

  @Override
  public String getUploadUrl(String path, String name, Long size) {
    return null;
  }

  /**
   * 检查路径合法性：
   *  - 只有以 . 开头的允许通过，其他的如 ./ ../ 的都是非法获取上层文件夹内容的路径.
   * @param   paths
   *          文件路径
   * @throws IllegalArgumentException    文件路径包含非法字符时会抛出此异常
   */
  private static void checkPathSecurity(String... paths) {
    for (String path : paths) {
      // 路径中不能包含 .. 不然可能会获取到上层文件夹的内容
      if (StrUtil.containsAny(path, "../", "..\\")) {
        throw new IllegalArgumentException("文件路径存在安全隐患: " + path);
      }
    }
  }


  /**
   * 检查路径合法性：
   *  - 不为空，且不包含 \ / 字符
   * @param   names
   *          文件路径
   * @throws IllegalArgumentException    文件名包含非法字符时会抛出此异常
   */
  private static void checkNameSecurity(String... names) {
    for (String name : names) {
      // 路径中不能包含 .. 不然可能会获取到上层文件夹的内容
      if (StrUtil.containsAny(name, "\\", "/")) {
        throw new IllegalArgumentException("文件名存在安全隐患: " + name);
      }
    }
  }
}
